package cn.net.shuxing.yifu.common.service;

import cn.net.shuxing.yifu.common.bean.*;
import cn.net.shuxing.yifu.common.constant.*;
import cn.net.shuxing.yifu.common.dto.BaseDto;
import cn.net.shuxing.yifu.common.dto.DealRecordDto;
import cn.net.shuxing.yifu.common.mapper.HangBuyMapper;
import cn.net.shuxing.yifu.common.util.*;
import com.alibaba.fastjson.JSON;
import com.github.pagehelper.PageHelper;
import org.apache.ibatis.session.RowBounds;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import cn.net.shuxing.yifu.common.util.StringUtils;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.ScanParams;
import redis.clients.jedis.ScanResult;
import tk.mybatis.mapper.entity.Example;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;

@Service
public class HangBuyService extends HistoryService<HangBuy, HangBuyMapper> {

    @Autowired
    private UserDealHistoryService userDealHistoryService;

    @Autowired
    private FeeService feeService;

    @Autowired
    private UserRealInfoService userRealInfoService;

    @Autowired
    private UserInfoBindService userInfoBindService;

    @Autowired
    private UserCapitalService userCapitalService;

    @Autowired
    private DealParamService dealParamService;

    @Autowired
    private UserService userService;

    @Autowired
    private CashWalletService cashWalletService;

    @Autowired
    private JedisUtil jedisUtil;

    @Autowired
    private CoinTypeService coinTypeService;

    @Autowired
    private UserRoleService userRoleService;

    @Override
    public HangBuy getByPrimaryKey(Object key) {
        return super.getByPrimaryKey(key);
    }

    public List<HangBuy> getTotalByUserId(Integer userId, Integer pageNum, Integer pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        CommonExample example = new CommonExample(HangBuy.class);
        example.equals("userId", userId);
        return getsByExample(example);
    }

    public List<HangBuy> getTotalByUserId(Integer userId) {
        CommonExample example = new CommonExample(HangBuy.class);
        example.equals("userId", userId);
        return getsByExample(example);
    }

    public List<HangBuy> getTotalByUserIdWithoutComplete(Integer userId, Integer pageNum, Integer pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        CommonExample example = new CommonExample(HangBuy.class);
        example.equals("userId", userId);
        List<HangBuy> hangBuys = getsByExampleWithoutComplete(example);
        if (hangBuys != null && !hangBuys.isEmpty()) {
            for (HangBuy hangBuy : hangBuys) {
                Integer coinTypeId = hangBuy.getCoinTypeId();
                hangBuy.setSimpleName(coinTypeService.getByPrimaryKey(coinTypeId).getSimpleName());
            }
        }
        return hangBuys;
    }

    public List<HangBuy> getTodayByUserId(int userId) {
        List<HangBuy> hangBuyList = getByToday();
        hangBuyList.stream().forEach(
                (x) -> {
                    if (!x.getUserId().equals(userId))
                        hangBuyList.remove(x);
                });
        return hangBuyList;
    }

    public BaseDto searchByRealName(String realName) {
        BaseDto baseDto = new BaseDto();
        List<UserRealInfo> userRealInfoList = userRealInfoService.getsByRealName(realName);
        List<HangBuy> hangBuyList = new ArrayList<>();
        for (UserRealInfo userRealInfo : userRealInfoList) {
            hangBuyList.addAll(getTotalByUserId(userRealInfo.getId()));
        }
        baseDto.setData(hangBuyList);
        return baseDto;
    }

    public BaseDto searchByPhoneNum(String phoneNum) {
        BaseDto baseDto = new BaseDto();
        try {
            UserInfoBind userInfoBind = userInfoBindService.getByPhoneNum(phoneNum);
            List<HangBuy> hangBuyList = getTotalByUserId(userInfoBind.getId());
            baseDto.setData(hangBuyList);
        } catch (Exception e) {
            baseDto.setCode(CodeConst.ERROR);
            baseDto.setMsg(MsgConst.REDIS_ERROR);
        }
        return baseDto;

    }

    public BaseDto searchByWantAliveCoinCount(BigDecimal low, BigDecimal high) {
        CommonExample example = new CommonExample(HangBuy.class);
        example.between("wantAliveCoinCount", low, high);
        List<HangBuy> hangBuyList = mapper.selectByExample(example);
        completeAll(hangBuyList);
        BaseDto baseDto = new BaseDto();
        baseDto.setData(hangBuyList);
        return baseDto;
    }

    public List<HangBuy> searchByPublishTime(LocalDateTime low, LocalDateTime high) {
        TimeExample example = new TimeExample(HangBuy.class);
        example.between("createTime", low, high);
        return mapper.selectByExample(example);
    }


    public BaseDto publishHangBuy(Integer userId, BigDecimal aliveCoinCount, BigDecimal singlePrice, Integer coinTypeId) {
        /**
         * 逻辑梳理:
         *  0. 判断当前用户是否正常,如果不是,报错
         *  1. 判断当前是否可以发布挂买单,如果不可以,报错
         *  2. 判断挂买单价是否符合规范,如果不符合,报错
         *  3. 判断当前现金余额是否够支付这笔挂买单,如果不够,报错
         *  4. 如果一切正常,扣除现金,发布挂买单
         *  5. 扣除手续费!!!
         */
        BaseDto baseDto = new BaseDto();
        try {
            /** 0. 判断当前用户是否正常,如果不是,报错 **/
            User user = userService.getByPrimaryKeyWithoutComplete(userId);
            Integer state = userRoleService.getUserStateByUserIdAndUserType(userId, UserType.MEMBER);
            if (state != UserState.OK) {
                baseDto.setCode(CodeConst.ERROR);
                baseDto.setMsg("当前状态无法发布挂买单");
                return baseDto;
            }

            if (user.getType() == UserType.NONE)
                return BaseDto.error("请先激活账号");

            if (user.getType() != UserType.MEMBER)
                return BaseDto.error("当前身份无法发布挂买单,请切换至会员身份");

            /** 1. 判断当前是否可以发布挂买单,如果不可以,报错 **/
            //Jedis //jedis = jedisUtil.getJedis();
            String able = jedisUtil.get(RedisKeyCreator.ablePublishHangBuy(userId));
            if (StringUtils.hasText(able)) {
                baseDto.setCode(CodeConst.ERROR);
                baseDto.setMsg("当前状态无法发布挂买单");
                //jedisUtil.close();
                return baseDto;
            }

            /** 2. 判断挂买单价是否符合规范,如果不符合,报错 **/
            /** 挂买单价 必须不低于 开盘价 的 最低挂买单价比例 **/

            /** 开盘价 **/
            BigDecimal openPrice = new BigDecimal(jedisUtil.get(RedisKeyCreator.openPrice(coinTypeId)));

            /** 最低挂买单价比例 **/
            String hangBuyOutPriceRatioStr = dealParamService.getValue(userId, coinTypeId, UserParamConst.HANG_BUY_OUT_PRICE_RATIO);
            BigDecimal minHangBuySinglePriceRate = new BigDecimal(hangBuyOutPriceRatioStr);

            /** 如果 singlePrice < openPrice * (minHangBuySinglePriceRate + 1) 就报错**/
            if (singlePrice.compareTo(openPrice.multiply((minHangBuySinglePriceRate).add(new BigDecimal(1)))) < 0) {
                //jedisUtil.close();
                baseDto.setCode(CodeConst.ERROR);
                baseDto.setMsg("挂买单价必须高于开盘价的" + minHangBuySinglePriceRate.toString());
                return baseDto;
            }
            //jedisUtil.close();

            /** 3. 判断当前现金余额是否够支付这笔挂买单,如果不够,报错 **/

            /** 当前买方钱包活币余额 **/
            BigDecimal cashCount = cashWalletService.getByPrimaryKey(userId).getCashCount();
            BigDecimal shouldPayAccount = singlePrice.multiply(aliveCoinCount);
            if (cashCount.compareTo(shouldPayAccount) < 0) {
                baseDto.setCode(CodeConst.ERROR);
                baseDto.setMsg("余额不足");
                return baseDto;
            }
            String orderNo = userDealHistoryService.createOrderNo(8);
            /** 扣除手续费 **/
            /** 4. 如果一切正常,扣除现金,发布挂买单 **/
            cashWalletService.reduceCash(userId, shouldPayAccount, CashChangeType.CASH_HANG_BUY, orderNo);

            BigDecimal feeCount = feeService.reduceFeeRetureFeeCount(userId, coinTypeId, DealScene.HANG_BUY, aliveCoinCount, singlePrice, UserType.MEMBER);
            /** 插入手续费记录**/
            userDealHistoryService.insert(userId, coinTypeId, DealType.FEE, DealScene.HANG_BUY, feeCount, singlePrice, UserType.MEMBER, orderNo);
//            /** 插入货币变动记录**/
//            userDealHistoryService.insert(userId,coinTypeId,DealType.HANG_BUY,DealScene.NONE,aliveCoinCount,singlePrice,UserType.MEMBER,uuid);
            /**生成挂买单**/
            HangBuy hangBuy = new HangBuy();
            hangBuy.setUserId(userId);
            hangBuy.setAliveCoinCount(aliveCoinCount);
            hangBuy.setCoinTypeId(coinTypeId);
            hangBuy.setSinglePrice(singlePrice);
            hangBuy.setAliveCoinCount(aliveCoinCount);
            hangBuy.setState(DealState.DEALING);
            hangBuy.setDealtCount(new BigDecimal(0));
            hangBuy.setSurplusCoinCount(aliveCoinCount);
            hangBuy.setOrderKey(orderNo);
            insert(hangBuy);
            hangBuy = super.getByPrimaryKey(hangBuy.getId());
            setInRedis(hangBuy);
            baseDto.setData(hangBuy);
            jedisUtil.set(RedisKeyCreator.ablePublishHangBuy(userId), "0");
            //jedisUtil.close();
        } catch (Exception e) {
            baseDto.setCode(CodeConst.ERROR);
            baseDto.setMsg(MsgConst.REDIS_ERROR);
            return baseDto;
        }
        return baseDto;
    }

    public void setInRedis(HangBuy hangBuy) {
        //Jedis //jedis = jedisUtil.getJedis();
        HangBuy byPrimaryKey = getByPrimaryKey(hangBuy.getId());
        if (byPrimaryKey != null)
            jedisUtil.srem(RedisKeyCreator.hangBuySet(), JSON.toJSONString(hangBuy));
        jedisUtil.sadd(RedisKeyCreator.hangBuySet(), RedisKeyCreator.hangBuyObj(hangBuy.getId()));
        //jedisUtil.close();
    }

    public void setHangBuyCompleted(HangBuy hangBuy) {
        hangBuy.setState(DealState.COMPLETED);
        hangBuy.setDealtCount(hangBuy.getAliveCoinCount());
        hangBuy.setSurplusCoinCount(new BigDecimal(0));
        update(hangBuy);
        //Jedis //jedis = jedisUtil.getJedis();
        jedisUtil.del(RedisKeyCreator.hangBuyObj(hangBuy.getId()));
        jedisUtil.srem(RedisKeyCreator.hangBuySet(), RedisKeyCreator.hangBuyObj(hangBuy.getId()));
        LocalDate createDate = LocalDateTimeUtil.getLocalDate(hangBuy.getCreateTime());
        // TODO: 2019/4/16 这个上线的时候要打开
//        if (!LocalDate.now().equals(createDate))
//            jedisUtil.del(RedisKeyCreator.ablePublishHangSale(hangBuy.getUserId()));
//        //jedisUtil.close();
        // TODO: 2019/4/16 这个上线的时候要注释掉
        LocalDateTime localDateTime = hangBuy.getCreateTime().plusMinutes(5);
        if (LocalDateTime.now().compareTo(localDateTime) >= 0) {
            jedisUtil.del(RedisKeyCreator.ablePublishHangSale(hangBuy.getUserId()));
        }
        //jedisUtil.close();
    }

    @Override
    public int update(HangBuy bean) {
        if (super.update(bean) < 1) {
            return -1;
        } else {
            setInRedis(bean);
            return 1;
        }
    }

    @Override
    public boolean delete(Object primaryKey) {
        //Jedis //jedis = jedisUtil.getJedis();
        jedisUtil.del(RedisKeyCreator.hangBuyObj((Integer) primaryKey));
        jedisUtil.srem(RedisKeyCreator.hangBuySet(), RedisKeyCreator.hangBuyObj((Integer) primaryKey));
        //jedisUtil.close();
        HangBuy hangBuy = getByPrimaryKey(primaryKey);
        hangBuy.setSurplusCoinCount(new BigDecimal(0));
        hangBuy.setState(DealState.COMPLETED);
        hangBuy.setDealtCount(hangBuy.getAliveCoinCount());
        super.delete(primaryKey);
        return true;
    }

    public void reduceSurplusCoinCount(Integer hangBuyId, BigDecimal aliveCoinCount) {
        HangBuy hangBuy = getByPrimaryKey(hangBuyId);
        BigDecimal surplusCoinCount = hangBuy.getSurplusCoinCount();
        surplusCoinCount = surplusCoinCount.subtract(aliveCoinCount);
        hangBuy.setSurplusCoinCount(surplusCoinCount);
        update(hangBuy);
    }


    @Override
    protected void complete(HangBuy hangBuy) {
        if (hangBuy == null)
            return;
        if (hangBuy.getState() == DealState.CANCEL)
            return;
        if (hangBuy.getSurplusCoinCount().compareTo(new BigDecimal(0)) <= 0) {
            hangBuy.setState(DealState.COMPLETED);
            return;
        }
        Integer coinTypeId = hangBuy.getCoinTypeId();
        String simpleName = coinTypeService.getByPrimaryKeyWithoutComplete(coinTypeId).getSimpleName();
        hangBuy.setSimpleName(simpleName);
    }

    public List<HangBuy> searchHangBuy(String phoneNum, String realName, BigDecimal lowAliveCount, BigDecimal highAliveCount, BigDecimal lowDealtCount, BigDecimal highDealtCount, LocalDate lowDate, LocalDate highDate) {
        List<Integer> userIdList = new ArrayList<>();
        List<HangBuy> resultList = new ArrayList<>();
        if (StringUtils.hasText(phoneNum)) {
            userIdList.add(userInfoBindService.getByPhoneNum(phoneNum).getId());
        } else {
            List<UserRealInfo> userRealInfoList = userRealInfoService.getsByRealName(realName);
            userRealInfoList.forEach((x) -> {
                userIdList.add(x.getId());
            });
        }
        for (Integer userId : userIdList) {
            CommonExample example = new CommonExample(HangBuy.class);
            Example.Criteria criteria = example.getCriteria();
            example.equals("userId", userId);
            if (lowAliveCount != null) {
                criteria.andGreaterThanOrEqualTo("aliveCount", lowAliveCount);
            }
            if (highAliveCount != null) {
                criteria.andLessThanOrEqualTo("aliveCount", highAliveCount);
            }
            if (lowDate != null) {
                LocalDateTime lowDateTime = LocalDateTimeUtil.createLocalDateTime(lowDate, LocalTimeUtil.createLocalTimeInZero());
                criteria.andGreaterThanOrEqualTo("createTime", lowDateTime);
            }
            if (highDate != null) {
                LocalDateTime highDateTime = LocalDateTimeUtil.createLocalDateTime(highDate.plusDays(1), LocalTimeUtil.createLocalTimeInZero());
                criteria.andLessThan("createTime", highDateTime);
            }
            List<HangBuy> hangBuyList = mapper.selectByExample(example);
//            completeAll(hangBuyList);
            if (lowDealtCount != null) {
                hangBuyList.forEach((x) -> {
                    if (x.getDealtCount().compareTo(lowDealtCount) < 0)
                        hangBuyList.remove(x);
                });
            }
            if (highDealtCount != null) {
                hangBuyList.forEach((x) -> {
                    if (x.getDealtCount().compareTo(highDealtCount) > 0)
                        hangBuyList.remove(x);
                });
            }
            resultList.addAll(hangBuyList);
        }
        return resultList;
    }

    public BaseDto cancel(Integer hangBuyId) {
        BaseDto baseDto = new BaseDto();
        HangBuy hangBuy = getByPrimaryKey(hangBuyId);
        if (hangBuy.getState() != DealState.CANCEL && hangBuy.getState() != DealState.COMPLETED) {
            cashWalletService.addCash(hangBuy.getUserId(), hangBuy.getSinglePrice().multiply(hangBuy.getSurplusCoinCount()), CashChangeType.CASH_HANG_BUY_CANSEL, hangBuy.getOrderKey());
            hangBuy.setState(DealState.CANCEL);
            update(hangBuy);
            //Jedis //jedis = jedisUtil.getJedis();
            jedisUtil.del(RedisKeyCreator.hangBuyObj(hangBuy.getId()));
            jedisUtil.srem(RedisKeyCreator.hangBuySet(), RedisKeyCreator.hangBuyObj(hangBuy.getId()));
            LocalDate createDate = LocalDateTimeUtil.getLocalDate(hangBuy.getCreateTime());
            // TODO: 2019/4/16  这个上线的时候要打开
//            if (!LocalDate.now().equals(createDate))
//                jedisUtil.del(RedisKeyCreator.ablePublishHangSale(hangBuy.getUserId()));
//            //jedisUtil.close();
//            baseDto.setData("撤单成功");
            // TODO: 2019/4/16 这个上线的时候要注释掉
            LocalDateTime localDateTime = hangBuy.getCreateTime().plusMinutes(5);
            if (LocalDateTime.now().compareTo(localDateTime) >= 0) {
                jedisUtil.del(RedisKeyCreator.ablePublishHangSale(hangBuy.getUserId()));
            }
            //jedisUtil.close();
        } else {
            baseDto.setCode(CodeConst.ERROR);
            baseDto.setMsg("此单已被撤销或已完成");
        }
        return baseDto;
    }


    public HangBuy getDealingByUserId(Integer userId) {
        HangBuy hangBuy = new HangBuy();
        hangBuy.clearCreateTimeAndUpdateTime();
        hangBuy.setState(DealState.DEALING);
        hangBuy.setUserId(userId);
        HangBuy hangBuy1 = get(hangBuy);
        if (hangBuy1 != null) {
            hangBuy1.setSimpleName(coinTypeService.getByPrimaryKey(hangBuy1.getCoinTypeId()).getSimpleName());
        }

        return hangBuy1;
    }

    public List<HangBuy> getHangBuyRank(Integer coinTypeId, int i) {
//        select * from hang_buy ORDER BY single_price asc LIMIT 1
        CommonExample example = new CommonExample(HangBuy.class);
        PageHelper.startPage(1, i);
        example.equals("coinTypeId", coinTypeId);
        example.equals("state", DealState.DEALING);
        example.setOrderByClause("single_price asc");
        List<HangBuy> hangBuyList = getsByExample(example);
        completeAll(hangBuyList);
        return hangBuyList;
    }

    public Integer getTotalCount() {
        return getAll().size();
    }

    public Integer getTodayCount() {
        return getByToday().size();
    }

    public List<HangBuy> getCompletedHangBuy(Integer userId, Integer coinTypeId, Integer pageNum, Integer pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        CommonExample example = new CommonExample(HangBuy.class);
        example.equals("userId", userId)
                .equals("coinTypeId", coinTypeId)
                .equals("state", DealState.COMPLETED);
        example.setOrderByClause("id DESC");
        List<HangBuy> hangBuys = getsByExample(example);
        for (HangBuy hangBuy : hangBuys) {
            hangBuy.setSimpleName(coinTypeService.getByPrimaryKey(coinTypeId).getSimpleName());
        }
        return hangBuys;
    }

    public List<HangBuy> getDealingHangBuy(Integer userId, Integer coinTypeId, Integer pageNum, Integer pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        CommonExample example = new CommonExample(HangBuy.class);
        example.equals("userId", userId)
                .equals("coinTypeId", coinTypeId)
                .equals("state", DealState.DEALING);
        example.setOrderByClause("id DESC");
        return getsByExample(example);
    }

    public List<HangBuy> getCancelHangBuy(Integer userId, Integer coinTypeId, Integer pageNum, Integer pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        CommonExample example = new CommonExample(HangBuy.class);
        example.equals("userId", userId)
                .equals("coinTypeId", coinTypeId)
                .equals("state", DealState.CANCEL);
        example.setOrderByClause("id DESC");
        List<HangBuy> hangBuys = getsByExample(example);
        for (HangBuy hangBuy : hangBuys) {
            hangBuy.setSimpleName(coinTypeService.getByPrimaryKey(coinTypeId).getSimpleName());
        }
        return hangBuys;
    }

    public List<HangBuy> getTotalByUserIdAndCoinType(Integer userId, Integer coinTypeId, Integer pageNum, Integer pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        CommonExample example = new CommonExample(HangBuy.class);
        example.equals("userId", userId)
                .equals("coinTypeId", coinTypeId);
        example.setOrderByClause("id DESC");
        List<HangBuy> hangBuys = getsByExample(example);
        for (HangBuy hangBuy : hangBuys) {
            hangBuy.setSimpleName(coinTypeService.getByPrimaryKey(coinTypeId).getSimpleName());
        }
        return hangBuys;
    }

    public List<HangBuy> getTotalByUserIdAndCoinType(Integer userId, Integer coinTypeId) {
        CommonExample example = new CommonExample(HangBuy.class);
        example.equals("userId", userId)
                .equals("coinTypeId", coinTypeId);
        example.setOrderByClause("id DESC");
        return getsByExample(example);
    }

    public List<HangBuy> getTodayByUserIdAndCoinTypeId(Integer userId, Integer coinTypeId) {
        CommonExample example = new CommonExample(HangBuy.class);
        example.equals("userId", userId);
        example.equals("coinTypeId", coinTypeId);
        Example.Criteria criteria = example.getCriteria();
        criteria.andGreaterThanOrEqualTo("createTime", LocalDateTimeUtil.getToday());
        return getsByExample(example);
    }

    public List<HangBuy> getByState(int state) {
        CommonExample example = new CommonExample(HangBuy.class);
        example.equals("state", state);
        return getsByExampleWithoutComplete(example);
    }

    public List<DealRecordDto> getByCoinTypeIdAndState(Integer coinTypeId) {
        CommonExample example = new CommonExample(HangBuy.class);
        example.equals("coinTypeId", coinTypeId);
        example.equals("state", DealState.DEALING);
        List<HangBuy> hangBuys = getsByExampleWithoutComplete(example);
        ArrayList<DealRecordDto> listDto = new ArrayList<>();
        if (hangBuys != null && !hangBuys.isEmpty()) {
            for (HangBuy hangBuy : hangBuys) {
                DealRecordDto dealRecordDto = new DealRecordDto();
                dealRecordDto.setDealType(DealType.HANG_BUY);
                dealRecordDto.setDealAliveCoinCount(hangBuy.getAliveCoinCount());
                dealRecordDto.setSurplusCount(hangBuy.getSurplusCoinCount());
                dealRecordDto.setCreateTime(hangBuy.getCreateTime());
                dealRecordDto.setDealPrice(hangBuy.getSinglePrice());
                listDto.add(dealRecordDto);
            }
        }
        return listDto;
    }
}
